/* 
 * File:   player.cpp
 * Author: iezahel
 * 
 * Created on December 27, 2008, 12:58 PM
 */

#include <netinet/in.h>
#include <SDL/SDL_net.h>

#include "ball.h"

#include "player.h"
#include "game_protocol.h"

#include <assert.h>

Player::Player(SDL_Surface* player, SDL_Surface* screen, NetworkCommunication* net_com, 
               int x_pos, int speed) {
    player_surface = player;
    screen_surface = screen;

    comm = net_com;

    player_w = player_surface->w;
    player_h = player_surface->h;

    screen_w = screen_surface->w;
    screen_h = screen_surface->h;

    velocity = 0.0;
    x = ((x_pos == GAME_PLAYER_POS_LEFT) ? 0 : screen_w - player_w);
    y = screen_h / 2 - player_h / 2; /* Put it in the center of the screen */

    default_player_speed = speed;
    
    score = 0;
    is_paused = false;
}

Player::Player(const Player& orig) {
}

Player::~Player() {
    if (hostname) free(hostname);
}

GAME_ERROR_CODES Player::HandleKeyboardInput(SDL_Event* event)
{
    GAME_ERROR_CODES err = GAME_ERROR_NONE;
    int old_velocity = velocity;

    if (event->type == SDL_KEYDOWN) { /* If a key was pressed */
        switch( event->key.keysym.sym )
        {
            case SDLK_UP:
                if (velocity == 0)
                    velocity -= default_player_speed;
                break;
            case SDLK_DOWN:
                if (velocity == 0)
                    velocity += default_player_speed;                
                break;
            case SDLK_p:
                is_paused = !is_paused;

                /* Inform the other player for our pause state (Blocking call) */
                if (is_paused) comm->SendPauseCommand();
                else comm->SendResumeCommand();
                
                break;
        }
    }
    else if (event->type == SDL_KEYUP) { /* If a key was released */
        switch( event->key.keysym.sym )
        {
            case SDLK_UP:
                if (velocity < 0)
                    velocity += default_player_speed;
                break;
            case SDLK_DOWN:
                if (velocity > 0)
                    velocity -= default_player_speed;
                break;
        }
    }

    if (old_velocity != velocity) { /* Send it through the network */
        err = comm->SendPlayerStateCommand(velocity); /* This is a blocking call */
    }

    return err;
}

GAME_ERROR_CODES Player::HandleNetworkInput(SDL_Event* event)
{
    GAME_ERROR_CODES err = GAME_ERROR_NONE;

    if (event->type == SDL_USEREVENT && event->user.code == GAME_COMMAND_PLAYER_STATE) {
        GameCommand* command = (GameCommand*)(event->user.data1);
        
        if (command->version != PROTOCOL_VERSION) { /* Verify the version protocol */
            err = GAME_ERROR_BAD_OPPONENT;
        }
        else {            
            velocity = SDLNet_Read32(command->data); /* Get the velocity */            
            int pos_y = SDLNet_Read32(command->data + 4); /* Get the position */            
            
            if (pos_y != -1) {
                y = pos_y;
            }
        }
    }

    return err;
}

/*
 * Checks whether the new ball position will collide with the player position.
 */
bool Player::CheckBallCollision(Ball* ball)
{
    SDL_Rect rect;
    rect.x = x;
    rect.w = player_surface->w;        

    int vec = (x == 0) ? +1 : -1; /* The ball orientation */
    int speed_x = vec * ball->GetSpeedX();

    /* Check player middle part (middle 90%) */
    rect.y = y + 0.05 * (player_surface->h);
    rect.h = 0.95 * (player_surface->h);

    if (ball->CheckCollision(&rect)) {
        ball->SetVelocityX(speed_x);
        ball->SetVelocityY(ball->GetVelocityY());        
        return true;
    }

    /* Check player upper part (the upper 5%) */
    rect.y = y;
    rect.h = 0.05 * (player_surface->h);
    
    if (ball->CheckCollision(&rect)) {
        ball->SetVelocityX(speed_x);
        ball->SetVelocityY(-ball->GetSpeedY());
        return true;
    }        

    /* Check player bottom part (bottom 5%) */
    rect.y = y + 0.95 * (player_surface->h);
    rect.h = 0.05 * (player_surface->h);

    if (ball->CheckCollision(&rect)) {
        ball->SetVelocityX(speed_x);
        ball->SetVelocityY(ball->GetSpeedY());
        return true;
    }

    return false;
}

double Player::distance_to_ball(const Ball* ball) const { /* not so accurate distance to the ball */
    int center_x = ball->GetX() + ball->GetRadius();
    int center_y = ball->GetY() + ball->GetRadius();

    double dist = 0;
    int player_x = (x == 0) ? x + player_w : x;
       
    if (!(ball->GetY() > y + player_w || ball->GetY() + 2 * ball->GetRadius() < y)) {
        dist = abs(player_x - ((x == 0) ? ball->GetX() : ball->GetX() + 2*ball->GetRadius()));
    }
    else if(ball->GetY() > y + player_w) {
        dist = abs(ball->GetY() - y - player_w);
    }
    else {
        dist = abs(ball->GetY() - 2 * ball->GetRadius() - y);
    }
    

    return dist;
}

bool Player::BallIsInDangerRegion(const Ball* ball) const
{
    bool toward_me = (x == 0 && ball->GetSpeedX() < 0) ||
                     (x > 0 || ball->GetSpeedX() > 0);

    if (!toward_me && !ball->IsStill()) return false;
    
    int dy = 8 * ball->GetRadius();
    
    int danger_w = 8 * ball->GetRadius();
    int danger_h = player_h + 2 * dy;

    int danger_x = (x == 0) ? 0 : x - danger_w;
    int danger_y = y - dy;

    SDL_Rect danger_rect = {danger_x, danger_y, danger_w, danger_h};

    return ball->CheckCollision(&danger_rect);
}

void Player::move(int delta_t)
{    
    int screen_h = screen_surface->h; 
    int player_h = player_surface->h;
   
    y += (velocity) * (delta_t / 1000.0);
    if (y < 0) y = 0;
    else if (y + player_h > screen_h) y = screen_h - player_h;
}

void Player::draw() {
    SDL_Rect offset;

    offset.x = (int)x;
    offset.y = (int)y;
    
    SDL_BlitSurface( player_surface, NULL, screen_surface, &offset );
}

void Player::draw_text_on_screen(TTF_Font* game_font, const char* text, int text_x, int text_y) {
    SDL_Color color = {0xFF,0xFF,0xFF};
    SDL_Surface *text_surface;
    
    text_surface = TTF_RenderText_Solid(game_font, text, color);
    if (text_surface != NULL)
    {
        SDL_Rect dest_rect = {text_x, text_y, 0, 0};
        SDL_BlitSurface(text_surface, NULL, screen_surface, &dest_rect);
        SDL_FreeSurface(text_surface);
    }    
}

void Player::draw_score(TTF_Font* game_font) {
    char text[16];
    sprintf(text, "%d", score);        

    int res_x = (x == 0) ? screen_w / 4 : 3 * screen_w / 4;
    int res_y = 0;
    
    draw_text_on_screen(game_font, text, res_x, res_y);
}

void Player::draw_pause_msg(TTF_Font* game_font) {
    int res_x = (x == 0) ? screen_w / 8 : 5 * screen_w / 8;
    int res_y = screen_h / 2;

    draw_text_on_screen(game_font, "PAUSED", res_x, res_y);
}


